#pragma once

#include "bsp/board_config.h"
#include "utils/byte_queue.h"

#if CONFIG_SERIAL_TYPE == SHARK_SERIAL_BOOTLOADER
#define CAN_BUILD_CMD(cmd) \
	(0x4D << 8 | (cmd))
#else
#define CAN_BUILD_CMD(cmd) \
	(CONFIG_ADDR_ID << 8 | (cmd))
#endif  /* CONFIG_SERIAL_TYPE == SHARK_SERIAL_BOOTLOADER */

#define SHARK_CAN_DEST_OFFSET		0
#define SHARK_CAN_SRC_OFFSET			7

#define SHARK_CAN_ALL_ID				0x7F
#define SHARK_CAN_BAUDRATE			250

#define SHARK_CAN_DEST_ID(id) \
	((u32)(id) << SHARK_CAN_DEST_OFFSET)

#define SHARK_CAN_SRC_ID(id) \
	((u32)(id) << SHARK_CAN_SRC_OFFSET)

#define FRAME_MAX_LEN 8
#pragma anon_unions
typedef struct {
	union {
		struct {
			u32 dest 		:7;
			u32 src			:7;
			u32 index		:5;
			u32 total		:5;
			u32 type		:2;
			u32 priority	:3;
			u32 length		:3;
		};
		u32 efid;
	};
	union {
		struct {
			u32 data0;
			u32 data1;
		};
		u8 data[FRAME_MAX_LEN];
	};
} can_frame_t;

typedef struct {
	u8 type;
	u8 dest;
	u8 src;
	u8 index;
	u16 times;
	u16 length;

	union {
		struct {
			u16 key;
			u8 args[0];
		};
		u8 data[256];
		u8 frames[32][8];
	};
}can_pack_t;

extern can_pack_t can_packs[CONFIG_CAN_PKG_COUNT];
extern can_frame_t can_rx_frames[CONFIG_CAN_TX_FRAMES];
extern can_frame_t can_tx_frames[CONFIG_CAN_RX_FRAMES];

extern byte_queue_t can_tx_allocator;
extern byte_queue_t can_rx_allocator;
extern byte_queue_t can_tx_queue;
extern byte_queue_t can_rx_queue;

void can_device_config(void);
void can_device_deinit(void);

u8 can_find_mailbox(void);
u8 can_wait_mailbox(void);
void can_poll(void);
bool can_poll_tx(can_frame_t *frame);
bool can_poll_rx(can_frame_t *frame);

bool can_send_frame(u32 efid, const void *buff);
bool can_send(u8 type, u8 dest, u8 src, const u8 *data, u16 length);
bool can_send_command(u8 addr, const void *command, u16 length);
bool can_send_command_read_status(void);
bool can_send_response(u8 dest, u8 src, const void *response, u16 length);
bool can_send_event(u8 addr, const void *event, u16 length);
bool can_process_uart_frame(u8 type, u8 dest, u8 src, const u8 *frame, u16 length);

void can_dev_init(void);
void can_dev_config(void);
void can_dev_deinit(void);
bool can_dev_tx_frame(can_frame_t *frame);
bool can_dev_get_error(void);
void can_hw_init(void);

